/*
 * sample C
 * syntax analysis with error recovery
 * symbol table
 * memory allocation
 * (shift/reduce conflics: one on ELSE
 */

%{
  #include "interprete.h"
  #include "symbol.h"
  #include "memassign.h"
  FILE *yyerfp;             /* puntero al fichero de errores */
  int numerito = 0;
%}

%union {
       WORDPTR ptr;         /* Identifier */
       long lvalue;         /* Constant */
       int ivalue;          /* Constant */
       int label;           /* labels */
}

%token <ptr> ID
%token <ivalue> ICONST
%token <lvalue> LCONST 
%token <ptr> INT
%token <ptr> LONG
%token IF
%token ELSE
%token WHILE
%token BREAK
%token CONTINUE
%token RETURN
%token GE       /* >= */
%token LE       /* <= */
%token EQ       /* == */
%token NE       /* != */
%token PE       /* += */
%token ME       /* -= */
%token TE       /* *= */
%token DE       /* /= */
%token RE       /* %= */
%token PP       /* ++ */
%token MM       /* -- */
%token ENUM
/*
 *  precedence table
 */
%right    '=' PE ME TE DE RE
%left     '|'
%left     '^'
%left     '&'
%left     EQ NE
%left     '<' '>' GE LE
%left     '+' '-'
%left     '*' '/' '%'
%right    PP MM


/*
 * typed non-terminal symbols 
 */
%type <ptr> type optionalParameterList parameterList binary argumentList enumerator restoList enumList enumType

%%
program      : {emit(JUMP,PC,0,4);}definitions                   { /* mostrar las variables globales */; 
                                               blockPop();
                                               emit(PUSH,0,0,g_offset);
                                               emit(CALL,PC,0,4);
                                               emit(POP,0,0,g_offset);
                                               emit(HALT,0,0,0);
                                             }
             ;

definitions  : definition
             | definitions definition        { yyerrok;}
             | error                        
             | definitions error            
             ;

definition   : {$<ptr>$ = lookUp("int");} functionDefinition  {;} 
             | type functionDefinition       {;}
             | declaration
             ;


type         : enumType{$$ = lookUp("enum");}
               |simpleType;

enumType     : ENUM {numerito =0;}'{' enumList '}';

enumList     : enumerator restoList;

restoList    : /*epsilon*/|',' enumList;

enumerator   : ID { addType(makeVar($1),lookUp("enum"));
                    $1->offset = numerito++;
                  }
             | ID '=' ICONST { addType(makeVar($1),lookUp("enum"));
                    numerito = $3;
                    $1->offset = numerito++;
                  }
;

simpleType   : INT | LONG;


functionDefinition : ID '('                  { $1=makeFunc($1);
                                               addType($1,$<ptr>0);
                                               blockPush(); 
					     }
                     optionalParameterList rightparenth 
                     parameterDeclarations   { checkParam($1, paramDefault($4) ); 
		                               /* mostrar todos los par�metros */
		                               /* inicializar el contador m�ximo */
                                                paramVarSize($1,$4);
                                             }
                     compoundStatement   
                                             { /* mostrar todas las variables locales */; 
                                               
                                               //copyParams($1,$4);
                                               
                                               //showTable(NULL);
                                               /* asociar los par�metros a las funciones */ 
                                               blockPop(); 
                                             }
                   ;

optionalParameterList :  /* no formal parameter */ { $$ = (WORDPTR) 0; /* no formal parameters */ }
                      |  parameterList             /* $$ = $1 = chain of formal parameters */
                      ;

parameterList         :  ID                      { $$ = linkParam( $1, (WORDPTR)0 ); }  
                      |  parameterList ',' ID    { $$ = linkParam( $3, $1);
                                                   yyerrok;
                                                 }
                      |  error                   { $$ = (WORDPTR) 0;  }
                      |  error parameterList     { $$ = $2; }
                      |  ID error parameterList  { $$ = linkParam( $1, $3); 
                                                   yyerrok; 
                                                 }
                      |  error ',' parameterList { $$ = $3;
                                                   yyerrok;
                                                 }
                      ;

parameterDeclarations :  /* null */
                      | parameterDeclarations parameterDeclaration  { yyerrok;}
                      | parameterDeclarations error  
                      ;

parameterDeclaration  : type parameterDeclaratorList semicolon      {;}
                      ;

parameterDeclaratorList : ID                                { addType(makeParam($1),$<ptr>0); }     
                        | parameterDeclaratorList ',' ID    { addType(makeParam($3),$<ptr>0);
                                                              yyerrok;
                                                            }
                        | error                             
                        | parameterDeclaratorList error     
                        | parameterDeclaratorList error ID  { addType(makeParam($3),$<ptr>0);
                                                              yyerrok;
                                                            }
                        | parameterDeclaratorList ',' error 
                        ;

compoundStatement     :  '{' { /* almacenar en la pila de YACC el contador de variables locales */ 
                               /* incrementar el nivel de profundidad */;
                               l_offset = 2;
                               blockPush();  
			     } 
                         declarations statements 
			 
			 rightbraces { /* recuperar el contador de variables locales anterior */
                                       /* decrementar el nivel de profundidad */;
                                       showTable(NULL);
                                       blockPop();  
                                     }
                      ;

declarations          : /* null */
                      | declarations declaration   { yyerrok;}
                      | declarations error         
                      ;

declaration           : type declaratorList semicolon {;}
                      ;

declaratorList        : ID                       { /* actualizar los desplz. de variables */
                                                   addType(makeVar($1),$<ptr>0); 
                                                   localVarSize($1);
						 }
                      | declaratorList ',' ID    { addType(makeVar($3),$<ptr>0);
                                                   localVarSize($3);
                                                   yyerrok;
                                                 }
                      | error
                      | declaratorList error ID  { addType(makeVar($3),$<ptr>0);
                                                   yyerrok;
                                                 }
                      | declaratorList ',' error 
                      ;

statements            : /* null */
                      | statements statement  { yyerrok;}
                      | statements error      
                      ;

statement             : expression semicolon
                      | semicolon                 /* null statement */
                      | BREAK semicolon
                      | CONTINUE semicolon
                      | RETURN semicolon
                      | RETURN expression semicolon
                      | compoundStatement
                      | ifPrefix statement                 
                      | ifPrefix statement ELSE statement  
                      | loopPrefix statement               
                      ;

ifPrefix              : IF '(' expression rightparenth
                      | IF error                            
                      ;

loopPrefix            : WHILE '(' expression rightparenth  
                      | WHILE error                         
                      ;

expression            : binary                   {;}
                      | expression ',' binary    { yyerrok; }
                      | error ',' binary         { yyerrok; }
                      | expression error         
                      | expression ',' error     
                      ;

binary                : ID                                        { checkVar($1); 
                                                                    $$ = getType($1);
                                                                    int reg = ($1->blockLevel == 1)?SB:LB;
                                                                    emit(LOAD,reg,$1->type->offset,$1->offset);
                                                                  }
                      | ICONST                                    { $$ = lookUp("int");
                                                                    emit(LOADL,0,0,$1);
                                                                  }
                      | LCONST                                    { $$ = lookUp("long");
                                                                    emit(LOADL,0,0,$1);
                                                                  }
                      | '(' expression rightparenth               {;}
                      | '(' error rightparenth                    {;}                      
                      | ID '('                                    { checkFunc($1);$<ptr>$ = $1->S.link; }
                               optionalArgumentList rightparenth  { ; }
                      | PP ID                                     { checkVar($2); 
                                                                    $$ = getType($2);
                                                                    int reg = ($2->blockLevel == 1)?SB:LB;
                                                                    emit(LOADA,reg,0,$2->offset);
                                                                    emit(CALL,PB,$2->type->offset,_INC);
                                                                    emit(LOAD,reg,$2->type->offset,$2->offset);
                                                                  }
                      | MM ID                                     { checkVar($2); 
                                                                    $$ = getType($2);
                                                                    int reg = ($2->blockLevel == 1)?SB:LB;
                                                                    emit(LOADA,reg,0,$2->offset);
                                                                    emit(CALL,PB,$2->type->offset,_DEC);
                                                                    emit(LOAD,reg,$2->type->offset,$2->offset);
                                                                  }
                      | binary '+' binary                         { $$ = checkType($1,$3);
                                                                    emit(CALL,PB,$$->offset,_ADD);
                                                                  } 
                      | binary '-' binary                         { $$ = checkType($1,$3);
                                                                    emit(CALL,PB,$$->offset,_SUB);
                                                                  } 
                      | binary '*' binary                         { $$ = checkType($1,$3);
                                                                    emit(CALL,PB,$$->offset,_MULT);
                                                                  } 
                      | binary '/' binary                         { $$ = checkType($1,$3);
                                                                    emit(CALL,PB,$$->offset,_DIV);
                                                                  } 
                      | binary '%' binary                         { $$ = checkType($1,$3);
                                                                    emit(CALL,PB,$$->offset,_MOD);
                                                                  } 
                      | binary '<' binary                         { $$ = checkType($1,$3);
                                                                    emit(CALL,PB,$$->offset,_LT);
                                                                  } 
                      | binary '>' binary                         { $$ = checkType($1,$3);
                                                                    emit(CALL,PB,$$->offset,_GT);
                                                                  } 
                      | binary GE  binary                         { $$ = checkType($1,$3);
                                                                    emit(CALL,PB,$$->offset,_GE);
                                                                  } 
                      | binary LE  binary                         { $$ = checkType($1,$3);
                                                                    emit(CALL,PB,$$->offset,_LE);
                                                                  } 
                      | binary EQ  binary                         { $$ = checkType($1,$3);
                                                                    emit(CALL,PB,$$->offset,_EQ);
                                                                  } 
                      | binary NE  binary                         { $$ = checkType($1,$3);
                                                                    emit(CALL,PB,$$->offset,_NE);
                                                                  } 
                      | binary '&' binary                         { $$ = checkType($1,$3);
                                                                    emit(CALL,PB,$$->offset,_AND);
                                                                  } 
                      | binary '^' binary                         { $$ = checkType($1,$3);
                                                                    emit(CALL,PB,$$->offset,_NOT);
                                                                  } 
                      | binary '|' binary                         { $$ = checkType($1,$3);
                                                                    emit(CALL,PB,$$->offset,_OR);
                                                                  } 
                      | ID                                        { checkVar($1); }
                           '=' binary                             { $$ = getType($1); 
                                                                    emit(CALL,PB,$$->offset,_ID);
                                                                    int reg = ($1->blockLevel == 1) ? SB:LB;
                                                                    emit(STORE,reg,$1->type->offset,$1->offset);
                                                                    if($1->type->offset < $4->offset)
                                                                       warning("detectada una perdida de precision");
                                                                     
                                                                  } 
                      | ID                                        { checkVar($1); 
                                                                    int reg = ($1->blockLevel == 1)?SB:LB;
                                                                    emit(LOAD,reg,$1->type->offset,$1->offset);
                                                                  }   
                           PE binary                              { $$ = getType($1); 
                                                                    emit(CALL,PB,$$->offset,_ADD);
                                                                    emit(CALL,PB,$$->offset,_ID);
                                                                    int reg = ($1->blockLevel == 1)?SB:LB;
                                                                    emit(STORE,reg,$1->type->offset,$1->offset);
                                                                  } 
                      | ID                                        { checkVar($1); }
                           ME binary                              { $$ = getType($1); } 
                      | ID                                        { checkVar($1); }
                           TE binary                              { $$ = getType($1); } 
                      | ID                                        { checkVar($1); }
                           DE binary                              { $$ = getType($1); } 
                      | ID                                        { checkVar($1); }
                           RE binary                              { $$ = getType($1); } 
                      ;

optionalArgumentList  : /* no actual arguments */  { ; /* # of actual arguments */ }
                      | argumentList               /* # of actual arguments */
                      ;

argumentList          : binary                     {
                                                    $$ = $<ptr>0;

                                                    if($$){
                                                    //show($<ptr>0);
                                                       //show($$->type);
                                                    //showTable(NULL);
                                                       //show($1);
                                                       checkType($$,$1);
                                                    }
                                                    else
                                                       error("too many argumetnts");
                                                    
                                       /* comprobar tipo formal y actual*/; }  
                      | argumentList ','{   if($1)
                                               if($1->S.link)
                                                  $1= $1->S.link;
                                               else
                                                  error("too many arguments");
                                         //showTable(NULL);
                                        } 
                        binary    { 
					if($1){
		                           //show($4);
                                           //show($1->type);
                                           checkType($4,$1->type);
                                        }
                                    yyerrok; }
                      | error                      { ; }
                      | argumentList error          
                      | argumentList ',' error      
                      ;

/*
 *  make certain terminal symbols very important
 */
semicolon      :  ';'         { yyerrok;}
               ;

rightparenth   :  ')'         { yyerrok;}
               ;

rightbraces    :  '}'         { yyerrok;}
               ;
%%

/*
 *  function main
 */

#include <string.h>
#include <stdio.h>

main( int argc, char ** argv )
{

  #ifdef DEBUG
    extern int yydebug;
  #endif

  extern FILE *yyin, *yyout;
  char *dest, *errf, *c;

  #ifdef DEBUG
    yydebug = 1;
  #endif

  dest = (char *) malloc(255);
  errf = (char *) malloc(255);

  if ( argc > 1 )
    strcpy( dest , argv[1]);
  else
  {
    printf("\nIntroduzca el nombre del Fichero Sample C: ");
    scanf("%s", dest);
  }

  yyin = fopen( dest, "r");
  if (!yyin)
  {
    fprintf( stderr, "no se pudo abrir el fichero %s\n", argv[1] );
    exit(1);
  }

  strcpy( errf , dest);
  c = strchr( errf, '.');
  strcpy(c, ".err");

  yyerfp = fopen( errf, "w");
  if (!yyerfp)
  {
    fprintf( stderr, "no se pudo abrir el fichero %s\n", errf );
    exit(1);
  }

  init();
  yyparse();
  fclose(yyin);
  fclose(yyout);
  fclose(yyerfp);

  if (yynerrs != 0)
    fprintf( stderr, "\n!!!!Errores  en el fichero %s!!!!!!!\n", errf);
  else
    fprintf( stderr, "\n!!!!Compilaci�n Correcta del fichero %s!!!!!!!\n", dest);
   imprime_codigo();
   interpreta();
} /* main */
